//============================================================================
// Copyright (C) 2013 Brett R. Jones
//
// Issued to public domain 2013
//
// brettjones1900@gmail.com
// http://www.p2panarchy.com
//============================================================================

#include "VxTimer.h"
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#ifdef WIN_PLATFORM
    #include <windows.h>
    #include <conio.h>
#else
    #include <sys/time.h>
#endif

//#define TEST_TIMER 1 //define this to use the test program

//============================================================================
void VxQueryCounter( unsigned long & u32RetRandNum )
{
	U64 s64Tick;
	VxTimer::queryPerformanceCounter( s64Tick );
	u32RetRandNum = (U32)s64Tick;
}

//============================================================================
void VxQueryCounter( U64 & i64RetRandNum )
{
	VxTimer::queryPerformanceCounter( i64RetRandNum );
}

bool	VxTimer::m_bCounterInitialized	= false;
bool	VxTimer::m_bHaveMultipleCPUS	= false;
U64		VxTimer::m_i64TimerFrequency	= 0;
double	VxTimer::m_dInverseFrequency	= 0.0;

//============================================================================--
VxTimer::VxTimer()
: m_dTimeTillDone(0)
{
	queryPerformanceCounter( m_i64StartTick );
}

//============================================================================--
VxTimer::~VxTimer()
{
}

//============================================================================--
void VxTimer::init( void )
{
	if( false == m_bCounterInitialized )
	{
		m_bHaveMultipleCPUS = false;

#ifdef WIN_PLATFORM
		SYSTEM_INFO sysinfo;
		GetSystemInfo(&sysinfo);
		m_bHaveMultipleCPUS = (sysinfo.dwNumberOfProcessors > 1);

		LARGE_INTEGER largeInt;
		QueryPerformanceFrequency( &largeInt );
		m_i64TimerFrequency = largeInt.QuadPart;
		m_dInverseFrequency = (1.0 / ((double)m_i64TimerFrequency));
#endif //_MSC_VER

		m_bCounterInitialized = true;
	}
}

//============================================================================--
void VxTimer::queryPerformanceCounter( U64& s64Tick )
{

#ifdef WIN_PLATFORM
	VxTimer::init();
	//get current tick count
	//currently the Counter Frequency for win95 intel is 1,193,180 Hz or approx .000000838 sec
	//a 32 bit counter will rollover in 3600 seconds or 1 Hour
	//the 64 bit counter will rollover in 4294967296 hours or 502,000 years

	// disable hires timer on multiple core systems, bios bugs result in bad hires timers.
	LARGE_INTEGER largeInt;
	DWORD_PTR affinityMask;
	if(m_bHaveMultipleCPUS)
	{
		affinityMask = SetThreadAffinityMask( GetCurrentThread(), 1 );
	}
    QueryPerformanceCounter( &largeInt );
	s64Tick = largeInt.QuadPart;
	// Restore the true affinity.
	if(m_bHaveMultipleCPUS)
	{
		(void)SetThreadAffinityMask(GetCurrentThread(), affinityMask);
	}

#else
    timeval tim;
    gettimeofday(&tim, NULL);
    s64Tick = tim.tv_usec + tim.tv_sec * 1000000;
#endif // _MSC_VER
}

//============================================================================--
void VxTimer::StartTimer( double dSec )
{
	queryPerformanceCounter( m_i64StartTick );
	m_dTimeTillDone = dSec * 1000000;
}

//============================================================================--
void VxTimer::WaitTime( double dMicroSec )
{
	//===for all other modes just loop and wait for time to run out ===//
   StartMicroTimer( dMicroSec  );
   int iFirstTime = TRUE;
   while(! TimerDone())
   {
	   // we want to sleep as much as possible so we dont waist cpu cycles but we
	   // want to be awake when timer is done
		if( iFirstTime )
		{
			iFirstTime = FALSE;
			if( dMicroSec > 2000 )
			{
				//VxSleep((long)(( dMicroSec -2000 ) / 1000.0));//should wake up 2ms before timer is done
				//usleep( 10 );
				VxSleep(1);
			}
			iFirstTime = 0;
		}
   }
}

//============================================================================--
double VxTimer::ElapsedSec( void )
{
	U64 i64EndTick;
	queryPerformanceCounter( i64EndTick );

#ifdef WIN_PLATFORM
	return (double)( i64EndTick-m_i64StartTick) * m_dInverseFrequency;
#else
	return (double)( i64EndTick-m_i64StartTick) / 1000000.0;
#endif // _MSC_VER
}

//============================================================================--
#ifdef TEST_TIMER
int main(int argc, char *argv[])
{

	VxTimer gTimer;
	double dDelay;
	long lLoopCnt;
	long i;
	while(TRUE)
	{
		printf("\n VxTimer Frequency = %lf", gTimer.GetTimerFrequency());
		printf("\n Enter Time To Delay in Micro Seconds ");
		scanf("%lf", &dDelay);
		printf("\n Enter The Number of Times to Delay ");
		scanf("%ld", &lLoopCnt);
		printf("\nNow Executing using TimerDone function");
		for(i = 0; i < lLoopCnt; i++)
		{
			gTimer.StartMicroTimer(dDelay);
			while(FALSE == gTimer.TimerDone())
				;
		}
		printf("\nNow Executing using Elapsed Time function");

		for(i = 0; i < lLoopCnt; i++)
		{
			gTimer.StartMicroTimer(0);
			while(dDelay > gTimer.ElapsedMicroSec())
				;
		}
		printf("\n*****************Finished******************");
		printf("\nPress X to exit or any other key to do again \n");
		fflush(stdin);
		int val = getch();
		if('X' == val || 'x' == val)
			return 0;
	}
}

#endif //TEST_TIMER


